import Utils (readInteger, tok, at, groupn) 

type CloseInterval = (Integer, Integer)
(a, b) `isin` (c, d) = (a >= c) && (b <= d)
(a, b) 'cuts' (c, _) = (a < c)  && (b >= c)

type Rule  = (CloseInterval, CloseInterval)
type Table = [Rule]

-- seeds: 79 14 55 13
getSeeds :: [String] -> [CloseInterval]
getSeeds = map tuplify
         . groupn 2
         . map readInteger
         . tok " "
         . at 1
         . tok ":"
         . head
    where
        tuplify xs = (xs!!0, (xs!!0) + (xs!!1) - 1)

getTables :: [String] -> [Table]
getTables = map (map parseRule)
          . map tail
          . tok [""]
          . drop 2

-- 0 69 1
parseRule :: String -> Rule
parseRule str = 
    (domain, image)
    where
        domain = (src, src + len - 1)
        image  = (dst, dst + len - 1)
        dst    = tokens !! 0
        src    = tokens !! 1
        len    = tokens !! 2
        tokens = map readInteger . tok " " $ str

apply :: Rule -> CloseInterval -> CloseInterval
apply ((u, v), (w, _)) (a, b)
    | (a, b) `isin` (u, v) = (a + w - u, b + w - u)
    | otherwise            = error "Rule not applicable"

transform :: Table -> CloseInterval -> [CloseInterval]
transform t (a, b)
    | rulesContain /= [] = [apply (head rulesContain) (a, b)]
    | rulesLeft    /= [] = transform t left1 ++ transform t right1
    | rulesRight   /= [] = transform t left2 ++ transform t right2
    | otherwise          = [(a, b)]

    where
        rulesContain = filter (((a, b) `isin`) . fst) $ t
        rulesLeft    = filter ((`cuts` (a, b)) . fst) $ t
        rulesRight   = filter (((a, b) `cuts`) . fst) $ t

        left1        = (a, cut1)
        right1       = (cut1 + 1, b)
        cut1         = snd . fst . head $ rulesLeft

        left2        = (a, cut2 - 1)
        right2       = (cut2, b)
        cut2         = fst . fst . head $ rulesRight

main = do
    contents <- readFile "input.txt"
    let seeds = getSeeds . lines $ contents
    let tables = getTables . lines $ contents

    print $ minimum
          . map fst
          . concat . map (transform (tables !! 6))
          . concat . map (transform (tables !! 5))
          . concat . map (transform (tables !! 4))
          . concat . map (transform (tables !! 3))
          . concat . map (transform (tables !! 3))
          . concat . map (transform (tables !! 1))
          . concat . map (transform (tables !! 0))
          $ seeds